#ifndef __OSAPICC18_H__
#define __OSAPICC18_H__


#undef OS_SMSG_SIZE
#define OS_SMSG_SIZE    sizeof(OST_SMSG)

/*************************************************************************************************************************/
//------------------------------------------------------------------------------
//    FSR = FSR0
static volatile near unsigned int _fsr @ 0xFE9;
static volatile near char         _indf @ 0xFEF;
static volatile near char         _postinc @ 0xFEE;
static volatile near char         _postdec @ 0xFED;
static volatile near char         _preinc @ 0xFEC; 

static volatile near unsigned char _fsr1l @ 0xFE1;

static volatile near char         _pcl @ 0xFF9;
static volatile near char         _pclath @ 0xFFA;
static volatile near char         _pclatu @ 0xFFB;
static volatile near char         _status @ 0xFD8;
static volatile near char         _tosl @ 0xFFD;
static volatile near char         _tosh @ 0xFFE;
static volatile near char         _tosu @ 0xFFF;
static volatile near char         _bsr @ 0xFE0;
static volatile near char         _wreg @ 0xFE8;
static volatile near char         _intcon @ 0xFF2;
static volatile near char         _rcon   @ 0xFD0;



#define _FSR_TYPE            unsigned int

#if defined(_ROMSIZE) && (_ROMSIZE <= 0x4000)
#define _PIC18_ERRATA_NOP()  
#else
#define _PIC18_ERRATA_NOP()  asm("\t    dw 0xFFFF     ")
#endif

#define _PIC18_POP()         asm("      pop     ");


//------------------------------------------------------------------------------
// Type of code pointer (used in OST_TCB)
#if defined(_ROMSIZE) && (_ROMSIZE <= 0x10000)
    #define OST_CODE_POINTER    unsigned int
    #define OS_CODE_POINTER_SIZE    2
#else
    #define OST_CODE_POINTER    unsigned long
    #define OS_CODE_POINTER_SIZE    4
#endif


#define OST_TASK_POINTER   OS_TASKS_BANK OST_TCB *



#define OS_CLRWDT()   asm(" clrwdt ");
#define OS_ClrWdt()   asm(" clrwdt ");
#define OS_SLEEP()    asm(" sleep  ");








//______________________________________________________________________________
/******************************************************************************
 * Switch control routines:
 *
 *  _OS_JumpToTask() - jump indirectly from kernel (OS_Sched) into task
 *  _OS_SaveRetAddr() - task return to this point 
 *
 *  _OS_GetReturnPoint() - exit from task and save context
 *  _OS_CallGetReturnPoint() - save context without exiting task
 *  _OS_ReturnNoSave() - exit task without saving context
 *
 *******************************************************************************/


#if defined(_ROMSIZE) && (_ROMSIZE <= 0x10000)
    #define _OS_SetPC()                         \
        asm("   movf    __postinc, w, c     "); \
        asm("   movff   __postinc, __pclath "); \
        asm("   movwf   __pcl, c            ");


    #define _OS_SavePC()                        \
        asm("   movff   __tosl, __preinc    "); \
        asm("   movff   __tosh, __preinc    ");

#else
    #define _OS_SetPC()                         \
        asm("   movf    __postinc, w, c     "); \
        asm("   movff   __postinc, __pclath "); \
        asm("   movff   __postinc, __pclatu "); \
        asm("   movwf   __pcl, c            ");
        

    #define _OS_SavePC()                        \
        asm("   movff   __tosl, __preinc    "); \
        asm("   movff   __tosh, __preinc    "); \
        asm("   movff   __tosu, __preinc    ");

#endif


    #define _OS_JumpToTask()                                                                    \
        {                                                                                       \
            asm("    bra     _OS_call_indirrect         ");                                     \
            asm("_OS_jump_indirrect:                    ");                                     \
            _OS_SET_FSR_CUR_TASK();                                                             \
            *((near char*)&OS_state) = _postinc;                                                \
            _OS_SetPC();                                                                        \
            asm("_OS_call_indirrect:                    ");                                     \
            asm("   rcall   _OS_jump_indirrect          ");                                     \
            _OS_SET_FSR_CUR_TASK();                                                             \
            asm("   movlw   0xD7                        ");/*except bReady and bCanContinue*/   \
            asm("   andwf   __indf, f, c                ");                                     \
            asm("   movlw   0x28                        ");/*bReady and bCanContinue*/          \
            asm("   andwf   _OS_state, w, c             ");                                     \
            asm("   iorwf   __indf, f, c                ");                                     \
        }

    #define _OS_SaveRetAddr()

    #define _OS_if_Ready()  _OS_SET_FSR_CUR_TASK();                                 \
                            if (_OS_bTaskReady)


extern void OS_SchedRetPoint (void);
extern void OS_SchedRetPointNoSave (void);
extern void OS_ClearReady (void);
extern void OS_ClearReadySetCanContinue (void);

extern void  _OS_SET_FSR_CUR_TASK (void);


//------------------------------------------------------------------------------
#define _OS_GetReturnPoint()                    \
    {                                           \
        OS_SchedRetPoint();                     \
        _PIC18_ERRATA_NOP();                    \
    }
    
//------------------------------------------------------------------------------
#define _OS_CLEAR_READY()                       \
    {                                           \
        OS_ClearReady();                        \
        _PIC18_ERRATA_NOP();                    \
    }
//------------------------------------------------------------------------------
#define _OS_CLEAR_READY_SET_CANCONTINUE()       \
    {                                           \
        OS_ClearReadySetCanContinue();          \
        _PIC18_ERRATA_NOP();                    \
    }
//------------------------------------------------------------------------------
#define _OS_ReturnNoSave()                      \
    {                                           \
        asm(" global _OS_SchedRetPointNoSave"); \
        OS_SchedRetPointNoSave();               \
        _PIC18_ERRATA_NOP();                    \
    }                                           \
//------------------------------------------------------------------------------








//______________________________________________________________________________
/******************************************************************************************
 *
 * MACROS FOR OLD STYLE STATIC TIMERS WORK
 *
 ******************************************************************************************/


#define __OS_Timer8Work(TIMER_ID, L)                                                                            \
    {                                                                                                           \
        if ((OS_Timeouts[(TIMER_ID)>>3]&(1<<((TIMER_ID)&7))))                                                   \
        {                                                                                                       \
            asm("\t    global   _OS_Timers8   ");                                                               \
            asm("\t    movlb    (_OS_Timers8 + (" #TIMER_ID")) >> 8 ");                                         \
            asm("\t    incfsz   (_OS_Timers8 + (" #TIMER_ID")) & 0xFF, f    ");                                 \
            asm("\t    bra      _TIMER_NEXT_8_"#TIMER_ID"_"#L"     ");                                          \
             OS_Timeouts[(TIMER_ID)>>3]&=~(1<<((TIMER_ID)&7));                                                  \
            asm("_TIMER_NEXT_8_"#TIMER_ID"_"#L": ");                                                            \
        }                                                                                                       \
    }

#define __OS_Timer16Work(TIMER_ID, L)                                                                           \
    {                                                                                                           \
        if ((OS_Timeouts[(TIMER_ID+_OS_TIMER16_POS)>>3]&(1<<((TIMER_ID+_OS_TIMER16_POS)&7))))                   \
        {                                                                                                       \
            asm("\t    global   _OS_Timers16  ");                                                               \
            asm("\t    movlb    (_OS_Timers16 + (" #TIMER_ID"*2)) >> 8 ");                                      \
            asm("\t    infsnz   (_OS_Timers16 + (" #TIMER_ID"*2)+0) & 0xFF, f    ");                            \
            asm("\t    incfsz   (_OS_Timers16 + (" #TIMER_ID"*2)+1) & 0xFF, f    ");                            \
            asm("\t    bra      _TIMER_NEXT_16_"#TIMER_ID"_"#L"     ");                                         \
             OS_Timeouts[(TIMER_ID+_OS_TIMER16_POS)>>3]&=~(1<<((TIMER_ID+_OS_TIMER16_POS)&7));                  \
            asm("_TIMER_NEXT_16_"#TIMER_ID"_"#L": ");                                                           \
        }                                                                                                       \
    }

#define __OS_Timer24Work(TIMER_ID, L)                                                                           \
    {                                                                                                           \
        if ((OS_Timeouts[(TIMER_ID+_OS_TIMER24_POS)>>3]&(1<<((TIMER_ID+_OS_TIMER24_POS)&7))))                   \
        {                                                                                                       \
            asm("\t    global   _OS_Timers24  ");                                                               \
            asm("\t    movlb    (_OS_Timers24 + (" #TIMER_ID"*2)) >> 8 ");                                      \
            asm("\t    infsnz   (_OS_Timers24 + (" #TIMER_ID"*2)+0) & 0xFF, f    ");                            \
            asm("\t    incfsz   (_OS_Timers24 + (" #TIMER_ID"*2)+1) & 0xFF, f    ");                            \
            asm("\t    bra      _TIMER_NEXT_24_"#TIMER_ID"_"#L"     ");                                         \
             OS_Timeouts[(TIMER_ID+_OS_TIMER24_POS)>>3]&=~(1<<((TIMER_ID+_OS_TIMER24_POS)&7));                  \
            asm("_TIMER_NEXT_24_"#TIMER_ID"_"#L": ");                                                           \
        }                                                                                                       \
    }

#define __OS_Timer32Work(TIMER_ID, L)                                                                           \
    {                                                                                                           \
        if ((OS_Timeouts[(TIMER_ID+_OS_TIMER32_POS)>>3]&(1<<((TIMER_ID+_OS_TIMER32_POS)&7))))                   \
        {                                                                                                       \
            asm("\t    global   _OS_Timers32  ");                                                               \
            asm("\t    movlb    (_OS_Timers32 + (" #TIMER_ID"*4)) >> 8 ");                                      \
            asm("\t    incf     (_OS_Timers32 + (" #TIMER_ID"*4)+0) & 0xFF, f    ");                            \
            asm("\t    bnc      _TIMER_NEXT_32_"#TIMER_ID"_"#L"     ");                                         \
            asm("\t    movlw    0     ");                                                                       \
            asm("\t    addwfc   (_OS_Timers32 + (" #TIMER_ID"*4)+1) & 0xFF, f    ");                            \
            asm("\t    addwfc   (_OS_Timers32 + (" #TIMER_ID"*4)+2) & 0xFF, f    ");                            \
            asm("\t    addwfc   (_OS_Timers32 + (" #TIMER_ID"*4)+3) & 0xFF, f    ");                            \
            asm("\t    bnc      _TIMER_NEXT_32_"#TIMER_ID"_"#L"     ");                                         \
             OS_Timeouts[(TIMER_ID+_OS_TIMER32_POS)>>3]&=~(1<<((TIMER_ID+_OS_TIMER32_POS)&7));                  \
            asm("_TIMER_NEXT_32_"#TIMER_ID"_"#L": ");                                                           \
        }                                                                                                       \
    }







//______________________________________________________________________________
/******************************************************************************************
 *
 * MACROS FOR WORK WITH TASK TIMERS
 *
 ******************************************************************************************/




#if   sizeof(OST_CODE_POINTER) == 2
    #if     OS_TTIMER_SIZE == 1
        #define ASM_OST_TCB_SIZE_CONST      asm("   OST_TCB_SIZE    equ     5");
    #elif   OS_TTIMER_SIZE == 2
        #define ASM_OST_TCB_SIZE_CONST      asm("   OST_TCB_SIZE    equ     6");
    #elif   OS_TTIMER_SIZE == 4
        #define ASM_OST_TCB_SIZE_CONST      asm("   OST_TCB_SIZE    equ     8");
    #else
        #error  "Bad OST_TIMER type (allowed 1-, 2- or 4-bytes type)!"
    #endif
#elif sizeof(OST_CODE_POINTER) == 4
    #if     OS_TTIMER_SIZE == 1
        #define ASM_OST_TCB_SIZE_CONST      asm("   OST_TCB_SIZE    equ     7");
    #elif   OS_TTIMER_SIZE == 2
        #define ASM_OST_TCB_SIZE_CONST      asm("   OST_TCB_SIZE    equ     8");
    #elif   OS_TTIMER_SIZE == 4
        #define ASM_OST_TCB_SIZE_CONST      asm("   OST_TCB_SIZE    equ    10");
    #else
        #error  "Bad OST_TIMER type (allowed 1-, 2- or 4-bytes type)!"
    #endif
#else
    #error  "Bad OST_CODE_POINTER type!"
#endif



// Set BSR for work with task timers 
#define ASM_SET_BANK    asm(" movlb      high(_OS_TaskVars)");   \
                        asm("OS_STATE_DELAY_BIT    equ  4");   \



// Expression compiles correctly in this case only:
//        asm("   movwf   ("#TASK_ID" +1 )* OST_TCB_SIZE + low(_OS_TaskVars) - 3 ");             

#if   OS_TTIMER_SIZE == 1
    #define __OS_TaskTimerWork(TASK_ID, L)                                                          \
        asm("   btfss   ("#TASK_ID" + 0)* OST_TCB_SIZE + low(_OS_TaskVars), OS_STATE_DELAY_BIT");   \
        asm("   bra     $ + 6");                                                                    \
        asm("   infsnz  ("#TASK_ID" + 1)* OST_TCB_SIZE + low(_OS_TaskVars) - 1     ");              \
        asm("   bcf     ("#TASK_ID" + 0)* OST_TCB_SIZE + low(_OS_TaskVars), OS_STATE_DELAY_BIT");


#elif OS_TTIMER_SIZE == 2
    #define __OS_TaskTimerWork(TASK_ID, L)                                                          \
        asm("   btfsc   ("#TASK_ID" + 0)* OST_TCB_SIZE + low(_OS_TaskVars), OS_STATE_DELAY_BIT");   \
        asm("   incfsz  ("#TASK_ID" + 1)* OST_TCB_SIZE + low(_OS_TaskVars) - 2     ");              \
        asm("   bra     $ + 6");                                                                    \
        asm("   infsnz  ("#TASK_ID" + 1)* OST_TCB_SIZE + low(_OS_TaskVars) - 1     ");              \
        asm("   bcf     ("#TASK_ID" + 0)* OST_TCB_SIZE + low(_OS_TaskVars), OS_STATE_DELAY_BIT");


#elif OS_TTIMER_SIZE == 4
    #define __OS_TaskTimerWork(TASK_ID, L)                                                          \
        asm("   btfsc   ("#TASK_ID" + 0)* OST_TCB_SIZE + low(_OS_TaskVars), OS_STATE_DELAY_BIT");   \
        asm("   incfsz  ("#TASK_ID" + 1)* OST_TCB_SIZE + low(_OS_TaskVars) - 4     ");              \
        asm("   bra     $ + 12");                                                                   \
        asm("   infsnz  ("#TASK_ID" + 1)* OST_TCB_SIZE + low(_OS_TaskVars) - 3     ");              \
        asm("   incfsz  ("#TASK_ID" + 1)* OST_TCB_SIZE + low(_OS_TaskVars) - 2     ");              \
        asm("   bra     $ + 6");                                                                    \
        asm("   infsnz  ("#TASK_ID" + 1)* OST_TCB_SIZE + low(_OS_TaskVars) - 1     ");              \
        asm("   bcf     ("#TASK_ID" + 0)* OST_TCB_SIZE + low(_OS_TaskVars), OS_STATE_DELAY_BIT");

#else

    #define __OS_TaskTimerWork(TASK_ID, L)                  \
        {                                                   \
            if (OS_TaskVars[TASK_ID].State.bDelay)          \
            {                                               \
                if (!++OS_TaskVars[TASK_ID].Timer)          \
                    OS_TaskVars[TASK_ID].State.bDelay = 0;  \
            }                                               \
        }

#endif      // if sizeof(OST_TIMER)

/******************************************************************************************/







 
 
/******************************************************************************************
 *
 *  WORK WITH DYNAMIC TIMERS
 *
 ******************************************************************************************/

#ifdef OS_ENABLE_DTIMERS


#if   OS_DTIMER_SIZE == 1
    #define OS_INC_DTIMER()                             \
        {                                               \
            asm("   movlw   3     ");                   \
            asm("   addwf   __fsr, f, c");  /* FSR0L can't overflow here */    \
            asm("   incf    __postdec, f, c     ");     \
            asm("   movf    __postdec, w, c     ");     \
            asm("   movf    __postdec, w, c     ");     \
            if (CARRY) _OS_SetIndfTimerTimeout;         \
        }

        
#elif OS_DTIMER_SIZE == 2
    #define OS_INC_DTIMER()                             \
        {                                               \
            _OS_DTimers.Flags.bTimeout = 0;             \
            asm("   movlw   3     ");                   \
            asm("   addwf   __fsr, f, c");  /* FSR0L can't overflow here */    \
            asm("   movlw   0 ");                       \
            asm("   incf    __postinc, f, c     ");     \
            asm("   addwfc  __postdec, f, c     ");     \
            if (CARRY) _OS_DTimers.Flags.bTimeout = 1;  \
            asm("   movlw   3 ");                       \
            asm("   subwf   __fsr, f, c"); /* FSR0L can't overflow here */    \
            if (_OS_DTimers.Flags.bTimeout)             \
                _OS_SetIndfTimerTimeout;                \
        }
#elif OS_DTIMER_SIZE == 4
    #define OS_INC_DTIMER()                             \
        {                                               \
            _OS_DTimers.Flags.bTimeout = 0;             \
            asm("   movlw   3     ");                   \
            asm("   addwf   __fsr, f, c");  /* FSR0L can't overflow here */    \
            asm("   movlw   0 ");                       \
            asm("   incf    __postinc, f, c     ");     \
            asm("   addwfc  __postinc, f, c     ");     \
            asm("   addwfc  __postinc, f, c     ");     \
            asm("   addwfc  __postdec, f, c     ");     \
            if (CARRY) _OS_DTimers.Flags.bTimeout = 1;  \
            asm("   movlw   5     ");                   \
            asm("   subwf   __fsr, f, c"); /* FSR0L can't overflow here */    \
            if (_OS_DTimers.Flags.bTimeout)             \
                _OS_SetIndfTimerTimeout;                \
        }
#endif


#define __OS_DTimersWork()                              \
    {                                                   \
        _fsr = (_FSR_TYPE) &_OS_DTimers.Flags;          \
    REPEAT:                                             \
        if (_OS_CheckPostincTimerNextEnable)            \
        {                                               \
            /* Point FSR to next timer in list   */     \
            asm("   movf    __postinc, w, c        ");  \
            asm("   movff   __postdec, __fsr + 1   ");  \
            asm("   movwf   __fsr + 0, c      ");       \
            if (!_OS_CheckIndfTimerRun) goto REPEAT;    \
            OS_INC_DTIMER();                            \
            goto REPEAT;                                \
        }                                               \
    }



#endif  // OS_ENABLE_DTIMER




/******************************************************************************************
 *
 *
 *
 ******************************************************************************************/





#endif
